package it.infodea.tapestrydea.services.jcr.assets;

import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
import it.infodea.tapestrydea.services.jcr.JcrSessionProviderService;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.ioc.internal.util.LocalizedNameGenerator;

/**
 * A resource stored with in the repository.
 */
@Deprecated
public class RepositoryResource implements Resource {

	private JcrSessionProviderService jcrSessionProvider;

	// Guarded by this
	private URL url;

	// Guarded by this
	private boolean urlResolved;

	private final String path;

	private String realPath;

	public RepositoryResource(JcrSessionProviderService jcrSessionProvider, String path) {
		notNull(jcrSessionProvider, "jcrSessionProvider");
		notNull(path, "path");
		this.path = path;
		this.realPath = path;
		this.jcrSessionProvider = jcrSessionProvider;
	}

	protected Resource newResource(String path) {
		if (!path.startsWith("/")) {
			path = "/" + path;
		}
		return new RepositoryResource(jcrSessionProvider, path);
	}

	public synchronized URL toURL() {
		//TODO [Marco]: Questo metodo va rivisto
		// sebbene funzioni correttamente è un po' confuso
		// il compito di questo metodo è quello di creare un 
		// oggetto java.net.URL che però non può essere espresso 
		// direttamente a partire dal repository, quindi occore 
		// creare un file temporaneo e linkare quello
		// la cosa è ulteriormente complicata dal fatto che la 
		// risorsa potrebbe essere sia un file del repository sia 
		// una sua versione, il cui path non è valido per il filesystem
		// e contiene spesso il carattere

		Session session = jcrSessionProvider.getSession();
		if (!urlResolved) {
			try {
				String path = getPath();
				if (session.itemExists(path)) {
					File tempFile = null;
					InputStream stream = null;
					FileOutputStream fos = null;
					File temp = null;
					try {
						Node node = (Node) session.getItem(path);

						if ("jcr:frozenNode".equals(node.getName())) {
							String uuid = node.getProperty("jcr:frozenUuid").getString();
							Node original = session.getNodeByUUID(uuid);
							realPath = original.getPath();
						}

						Property dataProperty = node.getNode("jcr:content").getProperty("jcr:data");
						stream = dataProperty.getStream();
						String userID = session.getUserID();
						String destDir = String.format("%s%s/%s", System.getProperty("java.io.tmpdir"), userID, getFolder());
						temp = new File(destDir);
						temp.mkdirs();
						String destFilePath = String.format("%s/%s", destDir, getFile());
						temp = new File(destFilePath);
						fos = new FileOutputStream(temp);
						byte[] buffer = new byte[stream.available()];
						stream.read(buffer);
						fos.write(buffer);
						fos.flush();

					} catch (PathNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (RepositoryException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (FileNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} finally {
						if (stream != null) {
							try {
								stream.close();
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
						if (fos != null) {
							try {
								fos.close();
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
					}
					tempFile = temp;
					try {
						url = tempFile.toURI().toURL();
					} catch (MalformedURLException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					urlResolved = true;
				}
			} catch (RepositoryException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		return url;
	}

	/**
	 * Opens the stream, wrapped by a BufferedInputStream.
	 */
	public InputStream openStream() throws IOException {
		if (exists()) {
			try {
				Session session = jcrSessionProvider.getSession();
				Node node = (Node) session.getItem(path);
				Property dataProperty = node.getNode("jcr:content").getProperty("jcr:data");
				return new BufferedInputStream(dataProperty.getStream());
			} catch (PathNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (RepositoryException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return null;
	}

	public final String getPath() {
		return path;
	}

	public final String getFile() {
		int slashx = realPath.lastIndexOf('/');

		return realPath.substring(slashx + 1);
	}

	public final String getFolder() {
		int slashx = realPath.lastIndexOf('/');

		return (slashx < 0) ? "" : realPath.substring(0, slashx);
	}

	public final Resource forFile(String relativePath) {
		Defense.notNull(relativePath, "relativePath");

		StringBuilder builder = new StringBuilder(getFolder());

		for (String term : relativePath.split("/")) {
			// This will occur if the relative path contains sequential slashes

			if (term.equals(""))
				continue;

			if (term.equals("."))
				continue;

			if (term.equals("..")) {
				int slashx = builder.lastIndexOf("/");

				// TODO: slashx < 0 (i.e., no slash)

				// Trim path to content before the slash

				builder.setLength(slashx);
				continue;
			}

			// TODO: term blank or otherwise invalid?
			// TODO: final term should not be "." or "..", or for that matter,
			// the
			// name of a folder, since a Resource should be a file within
			// a folder.

			if (builder.length() > 0)
				builder.append("/");

			builder.append(term);
		}

		return createResource(builder.toString());
	}

	public final Resource forLocale(Locale locale) {
		for (String path : new LocalizedNameGenerator(this.path, locale)) {
			Resource potential = createResource(path);

			if (potential.exists())
				return potential;
		}

		return null;
	}

	public final Resource withExtension(String extension) {
		notBlank(extension, "extension");

		int dotx = path.lastIndexOf('.');

		if (dotx < 0)
			return createResource(path + "." + extension);

		return createResource(path.substring(0, dotx + 1) + extension);
	}

	/**
	 * Creates a new resource, unless the path matches the current Resource's
	 * path (in which case, this resource is returned).
	 */
	private Resource createResource(String path) {
		if (this.path.equals(path))
			return this;

		return newResource(path);
	}

	/**
	 * Simple check for whether {@link #toURL()} returns null or not.
	 */
	public boolean exists() {
		try {
			Session session = jcrSessionProvider.getSession();
			return session.itemExists(path);
		} catch (RepositoryException e) {
			return false;
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null)
			return false;

		if (obj == this)
			return true;

		if (obj.getClass() != getClass())
			return false;

		RepositoryResource other = (RepositoryResource) obj;

		return other.getPath().equals(getPath());
	}

	@Override
	public int hashCode() {
		return 37 ^ getPath().hashCode();
	}

	@Override
	public String toString() {
		return "assets/repository:" + getPath();
	}

}
